package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.entity.SysUser;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.framework.config.properties.HuaWeiIotConfigProperties;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceDataService;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.IotMsgNotifyData;
import com.zzyl.nursing.vo.IotMsgService;
import com.zzyl.nursing.vo.ProductVo;
import com.zzyl.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备Service业务层处理
 * 
 * @author cisimu
 * @date 2025-10-23
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private HuaWeiIotConfigProperties huaWeiIotConfigProperties;
    @Autowired
    private IDeviceDataService deviceDataService;

    /**
     * 查询设备
     * 
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询设备列表
     * 
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return save(device) ? 1 : 0;
    }



    /**
     * 批量删除设备
     * 
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除设备信息
     * 
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 从物联网平台同步产品列表
     */
    @Override
    public void sysProductList() {
        try {
            //对接华为IOT，获取产品列表
            ListProductsResponse response = client.listProducts(new ListProductsRequest());
            if(response.getHttpStatusCode() != HttpStatus.HTTP_OK){
                throw new BaseException("同步产品列表失败");
            }
            List<ProductSummary> productSummaryList = response.getProducts();
            if(CollUtil.isNotEmpty(productSummaryList)){
                //将获取到产品列表转为json字符串，存到redis中
                String jsonStr = JSONUtil.toJsonStr(productSummaryList);
                redisTemplate.opsForValue().set(CacheConstants.IOT_PRODUCTS_LIST_KEY,jsonStr);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 查询所有产品列表
     *
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {
        //1.从redis中获取产品列表
        String jsonStr = redisTemplate.opsForValue().get(CacheConstants.IOT_PRODUCTS_LIST_KEY);
        if(StrUtil.isBlank(jsonStr)){
            return Collections.emptyList();
        }
        //2.将json字符串转为列表类型
        List<ProductSummary> productSummaryList = JSONUtil.toList(jsonStr, ProductSummary.class);
        //3.封装到vo列表
        List<ProductVo> productVoList = productSummaryList.stream().
                map(product -> ProductVo.builder().productKey(product.getProductId()).productName(product.getName()).build())
                .collect(Collectors.toList());
        return productVoList;
    }

    /**
     * 注册设备
     *
     * @param dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(DeviceDto dto) {
        //判断设备名称是否重复
        Long countByDeviceName = this.lambdaQuery().eq(Device::getDeviceName, dto.getDeviceName()).count();
        if(countByDeviceName>0){
            throw new BaseException("设备名称重复");
        }
        //判断设备标识码是否重复，前端参数没有标识码，使用雪花算法生成一个，肯定不会重复，不用判断
        String nodeId = IdUtil.getSnowflakeNextIdStr();
        //判断同一位置是否绑定了相同的设备
        Long countByLocation = this.lambdaQuery()
                .eq(Device::getLocationType, dto.getLocationType())
                .eq(Device::getBindingLocation, dto.getBindingLocation())
                .eq(Device::getProductKey, dto.getProductKey())
                .count();
        if(countByLocation>0){
            throw new BaseException("该位置已绑定重复设备");
        }
        //连接华为iot，注册设备
        AddDeviceRequest addDeviceRequest = new AddDeviceRequest();
        AddDevice addDevice = new AddDevice();
        AuthInfo authInfo = new AuthInfo();
        addDevice.setNodeId(nodeId);
        addDevice.setDeviceName(dto.getDeviceName());
        addDevice.setProductId(dto.getProductKey());
        addDevice.setDescription(dto.getNickname());
        //设备秘钥
        String secret = IdUtil.simpleUUID();
        authInfo.setSecret(secret);
        addDevice.setAuthInfo(authInfo);
        addDeviceRequest.setBody(addDevice);
        try {
            AddDeviceResponse response = client.addDevice(addDeviceRequest);
            if(response.getHttpStatusCode()!=HttpStatus.HTTP_CREATED){
                throw new BaseException("注册设备到华为云iot失败");
            }
            String iotId = response.getDeviceId();
            //本地数据库表新增设备
            Device device = BeanUtil.toBean(dto, Device.class);
            device.setNodeId(nodeId);
            device.setSecret(secret);
            device.setIotId(iotId);
            device.setHaveEntranceGuard(dto.getNickname().contains("门禁") ? 1 : 0);
            String productId = dto.getProductKey();
            String productName = getProductName(productId);
            if(StrUtil.isNotBlank(productName)){
                device.setProductName(productName);
            }
            this.save(device);
        } catch (BaseException e) {
            e.printStackTrace();
            throw new BaseException("注册设备到华为云iot失败");
        }
    }
    /**
     * 从缓存中根据产品id获取产品
     * @param productId
     * @return
     */
    private String getProductName(String productId) {
        //从redis获取到json字符串
        String jsonStr = redisTemplate.opsForValue().get(CacheConstants.IOT_PRODUCTS_LIST_KEY);
        if(StrUtil.isBlank(jsonStr)){
            //返回空
            return null;
        }
        //将json字符串转成集合
        List<ProductSummary> productSummaryList = JSONUtil.toList(jsonStr, ProductSummary.class);
        ProductSummary productSummary = productSummaryList.stream().filter(x -> productId.equals(x.getProductId())).findFirst().get();
        if(productSummary!=null){
            String productName = productSummary.getName();
            return productName;
        }
        return null;

    }

    /**
     * 获取设备详细信息
     *
     * @param iotId
     * @param productKey
     * @return
     */
    @Override
    public DeviceDetailVo queryDeviceDetail(String iotId, String productKey) {
        //1.从数据库表中读取设备数据
        Device device = this.lambdaQuery()
                .eq(Device::getIotId, iotId)
                .eq(Device::getProductKey, productKey)
                .one();
        if(device==null){
            throw new BaseException("设备表中设备不存在");
        }
        //2.从iot读取设备数据
        ShowDeviceRequest showDeviceRequest = new ShowDeviceRequest();
        showDeviceRequest.withDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(showDeviceRequest);
        if(response.getHttpStatusCode()!=HttpStatus.HTTP_OK){
            throw new BaseException("华为iot不存在该设备");
        }
        //3.将数据合并
        DeviceDetailVo vo = BeanUtil.toBean(device, DeviceDetailVo.class);
        vo.setDeviceSecret(device.getSecret());
        vo.setDeviceStatus(response.getStatus());
        vo.setRegion(huaWeiIotConfigProperties.getRegionId());
        vo.setFirmwareVersion(response.getFwVersion());
        vo.setGmtCreate(device.getCreateTime());
        SysUser sysUser = userService.selectUserById(Long.valueOf(device.getCreateBy()));
        vo.setCreator(sysUser.getUserName());
        String activeTime = response.getActiveTime();
        if(StrUtil.isNotBlank(activeTime)){
            LocalDateTime localDateTime = OffsetDateTime.parse(activeTime).atZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
            vo.setGmtActive(localDateTime);
        }
        //4.返回
        return vo;
    }

    /**
     * 查看指定产品的已发布物模型中的功能定义详情
     *
     * @param productKey
     */
    @Override
    public JSONObject queryProductModelInfo(String productKey) {
        //调用华为云iot
        ShowProductRequest showProductRequest = new ShowProductRequest();
        showProductRequest.withProductId(productKey);
        ShowProductResponse response = client.showProduct(showProductRequest);
        if(response.getHttpStatusCode()!=HttpStatus.HTTP_OK){
            throw new BaseException("华为云IOT平台产品不存在");
        }
        //解析响应数据，返回
        List<ServiceCapability> serviceCapabilities = response.getServiceCapabilities();
        if (CollUtil.isNotEmpty(serviceCapabilities)) {
            ServiceCapability serviceCapability = serviceCapabilities.get(0);
            JSONObject jsonObject = JSONUtil.parseObj(serviceCapability);
            return jsonObject;
        }
        return null;
    }


    /**
     * 查询指定设备的物模型运行状态
     *
     * @param deviceName
     * @param productKey
     * @return
     */
    @Override
    public Map<String, Object> queryDevicePropertyStatus(String deviceName, String productKey) {
        //1.从数据库查询设备信息
        Device device = this.lambdaQuery().eq(Device::getDeviceName, deviceName)
                .eq(Device::getProductKey, productKey)
                .one();
        if(device==null){
            throw new BaseException("表中设备不存在");
        }
        //2.从华为云iot查询设备影子
        ShowDeviceShadowRequest showDeviceShadowRequest = new ShowDeviceShadowRequest();
        showDeviceShadowRequest.withDeviceId(device.getIotId());
        ShowDeviceShadowResponse response = client.showDeviceShadow(showDeviceShadowRequest);
        if(response.getHttpStatusCode()!= HttpStatus.HTTP_OK){
            throw new BaseException("查询上报数据失败");
        }
        //3.解析响应数据
        List<DeviceShadowData> shadowDataList = response.getShadow();
        if(CollUtil.isEmpty(shadowDataList)){
            return Collections.emptyMap();
        }
        String eventTime = shadowDataList.get(0).getReported().getEventTime();
        if(StrUtil.isNotBlank(eventTime)){
            LocalDateTime reportTime = OffsetDateTime.parse(eventTime, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX"))
                    .atZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                    .toLocalDateTime();
            JSONObject propertyJsonObject = JSONUtil.parseObj(shadowDataList.get(0).getReported().getProperties());
            List<Map<String,Object>> propertyStatusInfoList = new ArrayList<>();
            propertyJsonObject.forEach((k,v)->{
                String propertyName = k;
                Object propertyValue = v;
                Map<String,Object> propertyMap = new HashMap<>();
                propertyMap.put("identifier",propertyName);
                propertyMap.put("value", propertyValue);
                propertyMap.put("time", reportTime);
                propertyStatusInfoList.add(propertyMap);
            });
            Map<String,Object> propertyStatusInfoMap = new HashMap<>();
            propertyStatusInfoMap.put("propertyStatusInfo", propertyStatusInfoList);

            Map<String, Object> listMap = new HashMap<>();
            listMap.put("list", propertyStatusInfoMap);
            return listMap;
        }
        return null;
    }



    /**
     * 修改设备备注名称
     *
     * @param dto 设备
     * @return 结果
     */
    @Override
    @Transactional
    public void updateDeviceNickName(DeviceDto dto)
    {
        //1.从数据库中查找设备
        Device device = this.selectDeviceById(dto.getId());
        if(device==null){
            throw new BaseException("该设备数据库不存在");
        }
        //2.更新数据库
        this.lambdaUpdate()
                .eq(Device::getId, dto.getId())
                .set(StrUtil.isNotBlank(dto.getNickname()),Device::getNickname, dto.getNickname())
                .set(dto.getLocationType()!=null,Device::getLocationType,dto.getLocationType())
                .set(dto.getBindingLocation()!=null,Device::getBindingLocation,dto.getBindingLocation())
                .set(StrUtil.isNotBlank(dto.getRemark()),Device::getRemark,dto.getRemark())
                .update();
        //3.调用iot接口更新
        UpdateDeviceRequest updateDeviceRequest = new UpdateDeviceRequest();
        updateDeviceRequest.withDeviceId(device.getIotId());
        UpdateDevice updateDevice = new UpdateDevice();
        updateDevice.setDescription(dto.getNickname());
        updateDeviceRequest.withBody(updateDevice);
        UpdateDeviceResponse response = client.updateDevice(updateDeviceRequest);
    }

    /**
     * 删除设备
     *
     * @param dto
     */
    @Override
    @Transactional
    public void delete(DeviceDto dto) {
        //1.删除表中数据
        Device device = this.lambdaQuery()
                .eq(Device::getIotId, dto.getIotId())
                .eq(Device::getProductKey, dto.getProductKey())
                .one();
        if(device == null){
            throw new BaseException("表中设备不存在");
        }
        this.deleteDeviceById(device.getId());
        //2.调用iot删除
        DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
        deleteDeviceRequest.withDeviceId(dto.getIotId());
        DeleteDeviceResponse deleteDeviceResponse = client.deleteDevice(deleteDeviceRequest);
        if(deleteDeviceResponse.getHttpStatusCode()!=HttpStatus.HTTP_NO_CONTENT){
            throw new BaseException("IoT删除失败");
        }
    }

    /**
     * 处理解析后的消息数据
     *
     * @param notifyData
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleMessageDate(IotMsgNotifyData notifyData) {
        //1.判断设备是否存在
        String deviceId = notifyData.getHeader().getDeviceId();
        Device device = this.lambdaQuery().eq(Device::getIotId, deviceId).one();
        if(device==null){
            throw new BaseException("设备不存在");
        }
        //2.获取上报的服务信息
        List<IotMsgService> serviceList = notifyData.getBody().getServices();
        serviceList.forEach(x->{
            //3.获取上报的物模型信息数据
            Map<String, Object> properties = x.getProperties();
            String eventTime = x.getEventTime();
            LocalDateTime alarmTime = OffsetDateTime.parse(eventTime, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX"))
                    .atZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                    .toLocalDateTime();

            List<DeviceData> deviceDataList = properties.entrySet().stream().map(entry -> {
                String functionId = entry.getKey();
                Object dataValue = entry.getValue();
                DeviceData deviceData = DeviceData.builder()
                        .deviceName(device.getDeviceName())
                        .iotId(deviceId)
                        .nodeId(device.getNodeId())
                        .nickname(device.getNickname())
                        .productKey(device.getProductKey())
                        .productName(device.getProductName())
                        .functionId(functionId)
                        .accessLocation(device.getBindingLocation())
                        .locationType(device.getLocationType())
                        .physicalLocationType(device.getPhysicalLocationType())
                        .dataValue(String.valueOf(dataValue))
                        .alarmTime(alarmTime)
                        .build();
                return deviceData;
            }).collect(Collectors.toList());
            //4.批量新增设备数据
            deviceDataService.saveBatch(deviceDataList);

            //将设备数据存入redis，hash类型
            redisTemplate.opsForHash().put(CacheConstants.IOT_DEVICE_LAST_DATA,device.getIotId(),JSONUtil.toJsonStr(deviceDataList));
        });
    }
}
